using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using EnvDTE80;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using EnvDTE;
using Microsoft.VisualStudio.TeamFoundation;

namespace TeamReview.Services
{
	public static class TFSService
	{
		private static TeamFoundationServer tfs;
		private static readonly List<string> users = new List<string>();
		private static readonly object userLock = new Object();
		private static TeamProject teamProject;
		private static WorkItemStore workItemStore;
		private static FieldDefinitionCollection fields;
		private static AllowedValuesCollection priorityAllowedValues;
		private static DTE2 shellDTE;
		private static SolutionEvents solutionEvents;
		public static event TeamProjectChangeEventHandler TeamProjectChanged;
		private static VersionControlServer versionControlServer;
		private static TeamFoundationServerExt tfsExt;
		private static VersionControlExt versionControl;

		private static void OnTeamProjectChanged()
		{

			if (teamProject == null)
				Instrumentation.Trace("TeamReview is not available because the current solution is not part of a Team Project.");
			else
				Instrumentation.Trace(String.Format(@"Team Project changed to {0}\{1}",
										  teamProject.TeamFoundationServer.Name, teamProject.Name));

			if (TeamProjectChanged == null)
				return;

			TeamProjectChanged();
		}


		public static VersionControlServer VersionControlServer
		{
			get { return versionControlServer; }
		}

		public static TeamProject TeamProject
		{
			get { return teamProject; }
		}

		public static VersionControlExt VersionControlExt
		{
			get { return versionControl; }
		}

		public static TeamFoundationServerExt TeamFoundationServerExt
		{
			get { return tfsExt; }
		}

		public static TeamFoundationServer TeamFoundationServer
		{
			get { return tfs; }
		}

		public static WorkItemStore WorkItemStore
		{
			get { return workItemStore; }
		}

		public static StoredQueryCollection StoredQueries
		{
			get
			{
				return workItemStore.Projects[teamProject.Name].StoredQueries;
			}
		}

		public static string CurrentUserDisplayName
		{
			get
			{
				if (workItemStore.UserDisplayMode == UserDisplayMode.AccountName)
					return workItemStore.TeamFoundationServer.AuthenticatedUserName;

				return workItemStore.TeamFoundationServer.AuthenticatedUserDisplayName;
			}
		}

		public static bool IsTFSProject
		{
			get
			{
				return !(teamProject == null);
			}
		}

		public static void Configure(DTE2 dte)
		{
			shellDTE = dte;
			solutionEvents = shellDTE.Events.SolutionEvents;
			solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
			SetTeamProject();
		}

		public static void Shutdown()
		{
			try
			{
				solutionEvents.Opened -= new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
			}
			catch { }
		}


		public static bool HasField(string fieldName)
		{
			return fields.Contains(fieldName);
		}


		static void SolutionEvents_Opened()
		{
			SetTeamProject();
		}



		public static AllowedValuesCollection PriorityAllowedValues
		{
			get { return priorityAllowedValues; }
		}

		public static void SetTeamProject()
    {

      TeamProject newProject;

      if (shellDTE.Solution == null)
        newProject = null;
      else
      {
          string solution = "not open";
          if (!String.IsNullOrEmpty(shellDTE.Solution.FullName))
              solution = shellDTE.Solution.FullName;

          Instrumentation.Trace(String.Format(@"Visual Studio Solution is {0}", solution));


        versionControl = shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt") as VersionControlExt;
        if (versionControl == null || versionControl.SolutionWorkspace == null)
          newProject = null;
        else
          newProject = versionControl.SolutionWorkspace.GetTeamProjectForLocalPath(shellDTE.Solution.FullName);
      }

      if (teamProject != newProject)
      {
        teamProject = newProject;

        if (teamProject == null)
        {
            OnTeamProjectChanged();
            return;
        }
       

        tfs = teamProject.TeamFoundationServer;
        tfsExt = (TeamFoundationServerExt)shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt");
        versionControlServer = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

        workItemStore = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
        if (workItemStore == null)
          throw new NullReferenceException("Did not retrieve WorkItemStore from Team Foundation Server");
        
		if(!workItemStore.Projects[teamProject.Name].WorkItemTypes.Contains(Constants.CODE_REVIEW_ITEM_TYPE))
		{
		  teamProject = null;
		  OnTeamProjectChanged();
		  return;
		}
          
        fields = workItemStore.Projects[teamProject.Name].WorkItemTypes[Constants.CODE_REVIEW_ITEM_TYPE].FieldDefinitions;

        if (fields.Contains(Constants.CODE_PRIORITY_FIELD))
        {
          priorityAllowedValues = fields[Constants.CODE_PRIORITY_FIELD].AllowedValues;

        }
        else
        {
          priorityAllowedValues = null;
        }

        System.Threading.Thread thread = new System.Threading.Thread(new ThreadStart(SetUsers));
        thread.Start();
        OnTeamProjectChanged();
      }
    }

		public static WorkItemCollection GetAvailableQueryWorkItems(Guid queryGuid)
		{
			StoredQuery scQuery = TFSService.WorkItemStore.GetStoredQuery(queryGuid);
			string project = String.Format("'{0}'", TFSService.TeamProject.Name);
			string me = String.Format("'{0}'", TFSService.TeamProject.TeamFoundationServer.AuthenticatedUserDisplayName);
			string text = scQuery.QueryText.Replace("@project", project).Replace("@me", me);
			Query query = new Query(TFSService.WorkItemStore, text);
			query.SortFieldList.Clear();
			query.SortFieldList.Add("System.Id", SortType.Ascending);

			return TFSService.WorkItemStore.Query(query.QueryString);
		}

		public static TeamProject GetTeamProject()
		{
			return teamProject;
		}

		private static void SetUsers()
		{
			lock (userLock)
			{
				if (tfs == null)
					return;

				users.Clear();

				IGroupSecurityService gss = (IGroupSecurityService)tfs.GetService(typeof(IGroupSecurityService));

				Identity[] projectGroups = gss.ListApplicationGroups(teamProject.ArtifactUri.ToString());
				Identity serviceGroup = gss.ReadIdentity(SearchFactor.ServiceApplicationGroup, null, QueryMembership.None);
				Identity[] serviceGroups = new Identity[1] { serviceGroup };

				AddCodeChangers(gss, projectGroups);
				AddCodeChangers(gss, serviceGroups);

				users.Sort();
			}
		}

		private static void AddCodeChangers(IGroupSecurityService gss, Identity[] projectGroups)
		{
			foreach (Identity projectGroup in projectGroups)
			{
				if (!(projectGroup.DisplayName.Equals("Contributors") ||
					  projectGroup.DisplayName.Equals("Project Administrators") ||
					  projectGroup.DisplayName.Equals("Service Accounts")))
					continue;

				Identity directMembers = gss.ReadIdentity(SearchFactor.Sid, projectGroup.Sid, QueryMembership.Expanded);
				foreach (string memberSid in directMembers.Members)
				{
					Identity member = gss.ReadIdentity(SearchFactor.Sid, memberSid, QueryMembership.None);

					if (!member.SecurityGroup && member.Type != IdentityType.WindowsGroup
						&& member.Type != IdentityType.ApplicationGroup)
					{
						if (!users.Contains(member.DisplayName))
							users.Add(member.DisplayName);
					}
				}
			}
		}

		public static List<string> GetTeamUsers()
		{
			lock (userLock)
			{
				return users;
			}
		}

		public static WorkItemTypeCollection GetAvailableTeamWorkItemTypes
		{
			get { return workItemStore.Projects[teamProject.Name].WorkItemTypes; }
		}
	}
}
